# -*-coding:utf-8-*-
'''
first step for funciton anlyzer is to analyze as few information as possible.
just realize :
    1.global parameters to read
    2.global parameters to write
    3.function call.

'''
import re
from c_code_base_analyzer import print_comment, \
    is_a_name, name_filter
from c_code_base_analyzer import c_code_reader


class function_analyzer(c_code_reader):
    def is_write_command(self, line_left):
        if re.match(r'([\+\[\]\-A-Za-z0-9_*. \t>]+)=(.*)',
                    line_left) is not None:

            if line_left[line_left.find('=') - 1] != '>' and line_left[
                    line_left.find('=') - 1] != '<':
                return True,line_left
        if line_left.find('[') > 0 and line_left.count('=') == 1 and line_left.count('<=') == 0 and line_left.count('>=') == 0 and line_left.count('!=') == 0:
            line_left = self.function_tmp.func_content[len(self.function_tmp.func_content)-1].strip()
            return True,line_left
        return False,line_left

    def is_defined_parameter(self, word):
        ret = False
        if word != "":
            for para_info in self.module_info.para_list:
                if name_filter(para_info.name) == name_filter(word):
                    ret = True
                    break
            for dec_name in self.module_info.declaration_list:
                if name_filter(dec_name) == name_filter(word):
                    ret = True
                    break
            for arg_name in self.function_tmp.arg_infos:
                if name_filter(
                        arg_name
                ) == '_arg_' + name_filter(word):
                    word = '_arg_' + name_filter(word)
                    ret = True
                    break
        return ret, word

    def is_defined_function(self, word):
        ret = False
        if word != "":
            for func_info in self.module_info.function_list:
                if name_filter(func_info.name) == name_filter(word):
                    ret = True
                    break
            for dec_name in self.module_info.declaration_list:
                if name_filter(dec_name) == name_filter(word):
                    ret = True
                    break
        return ret

    def is_local_para(self, word):
        ret = False
        if word != "":
            for para in self.function_tmp.local_para:
                if name_filter(para['name']) == name_filter(word):
                    ret = True
                    break
        return ret

    def local_para_append(self, line, type_name):
        is_ptr = False
        if line.find(',') > 0:
            names = line.split(',')
            for name in names:
                name = name.strip()
                if name.find('*') > 0:
                    is_ptr = True
                name = name[name.find('*') + 1:].strip()
                self.function_tmp.local_para.append({
                    "name": name,
                    "is_ptr": is_ptr,
                    "type": type_name
                })
                is_ptr = False

        else:
            if line.find('*') > 0:
                is_ptr = True
            name = line[line.find('*') + 1:].strip()
            self.function_tmp.local_para.append({
                "name": name,
                "is_ptr": is_ptr,
                "type": type_name
            })

    '''
    to assembly line, it can find ';' or { / }
    for the code, but it just can return at least a line. but not sentence.
    '''
    def get_code_sentence(self, line_left):
        sentences = []
        if line_left.count(';') > 1:
            sentences = re.split('([;])', line_left)
            i = 0
            while i < len(sentences):
                if sentences[i] == ';' and i > 0:
                    sentences[i - 1] = sentences[i - 1] + sentences[i]
                    sentences.remove(sentences[i])
                i += 1
        else:
            sentences.append(line_left)

        return sentences

    def line_anlyze(self, line_left):
        line_left = self.key_word_filter(line_left)
        '''
        if find type ,then it's local parameter definition.
        '''
        line_left, is_local_para_definition, type_name = self.find_type(
            line_left)
        is_write_cmd, line_left = self.is_write_command(line_left)
        if is_write_cmd:
            split_tmp = line_left.split('=')
            left_tmp = split_tmp[0].strip()
            if left_tmp[-1] == '-' or left_tmp[-1] == '+' or left_tmp[-1] == '|'or left_tmp[-1] == '&':
                left_tmp = left_tmp[:-1].strip()

            if is_local_para_definition is True:
                '''
                local para init
                '''
                self.local_para_append(left_tmp, type_name)
            else:
                '''
                write parameter append
                '''
                if not self.is_local_para(left_tmp):
                    for arg_name in self.function_tmp.arg_infos.keys():
                        if name_filter(
                                arg_name
                        ) == '_arg_' + name_filter(
                                left_tmp):
                            left_tmp = '_arg_' + left_tmp.strip(
                            ).replace('*', '')
                            break

                    self.function_tmp.write_paras.append(left_tmp.strip())
                '''
                find function forbiden
                '''
            line_left = split_tmp[1]
        elif is_local_para_definition is True and line_left.find(';') > 0:
            '''
            local para init
            '''
            self.local_para_append(line_left[:line_left.find(';')], type_name)
            return ""
        return self.read_symbol_anlyze(line_left)

    def read_symbol_anlyze(self, line_left):
        right_list = re.split(
            r'(\t|->|>>|==|<<|\+\+|\-\-|\+|\-|\*|\'|\"|\/|\,|\(|\)|\;|\!|\<|\>|\=|\?|\.|\[|\]|\||\:|\&)',
            line_left.strip().replace(' ', ''))

        for right_unit in right_list:
            if right_unit == '':
                right_list.remove(right_unit)

        unit_no = 0
        is_func_para = False
        is_skip_need = False
        brace_cnt = 0
        while unit_no < len(right_list):
            right_unit = right_list[unit_no]
            is_parameter, para_name = self.is_defined_parameter(right_unit)
            if not is_skip_need and right_unit == '(':
                if unit_no > 0:
                    if is_a_name(right_list[unit_no - 1]):
                        if self.is_defined_function(right_list[unit_no - 1]):
                            self.function_tmp.called_functions.append(
                                right_list[unit_no - 1])
                        is_func_para = True
                brace_cnt += 1
            elif not is_skip_need and is_parameter:
                if unit_no + 1 < len(right_list):
                    if right_list[unit_no + 1]=='(':
                        unit_no += 1
                        continue

                offest = 0
                wait_child_flag = False
                name_connected = False

                while unit_no + 1 < len(right_list) and            \
                    (right_list[unit_no+1] == '.' or
                     right_list[unit_no+1] == '->' or
                     wait_child_flag is True):
                    if name_connected is True:
                        wait_child_flag = False
                    if wait_child_flag is True or \
                            right_list[unit_no+1] == '.' or \
                            right_list[unit_no+1] == '->':
                        para_name += right_list[unit_no + 1]
                        if wait_child_flag is False:
                            wait_child_flag = True
                            name_connected = False
                        else:
                            name_connected = True
                        offest += 1
                        unit_no += 1
                is_write_para = False
                if unit_no + 1 < len(right_list) and \
                        (right_list[unit_no + 1] == '++' or right_list[unit_no + 1] == '--'):
                    unit_no += 1
                    is_write_para = True
                elif unit_no > 0:
                    if right_list[unit_no - offest -
                                  1] == '&' and is_func_para is True:
                        is_write_para = True
                if is_write_para:
                    self.function_tmp.write_paras.append(para_name)
                else:
                    self.function_tmp.read_paras.append(para_name)
            elif right_unit == ')':
                brace_cnt -= 1
                if (brace_cnt <= 0):
                    is_func_para = False
            elif right_unit == '\"' or right_unit == '\'':
                if not is_skip_need:
                    is_skip_need = True
                else:
                    is_skip_need = False

            unit_no += 1

        return ""

    func_line_cnt = 0

    def function_end_judge(self, line_left):
        if self.is_in_function is True and line_left[0] == '}':
            self.module_info.function_list.append(self.function_tmp)
            self.function_tmp = None
            self.is_in_function = False
            self.func_line_cnt = 0
            print_comment("function end")
            return ""
        else:
            return line_left

    def function_read_args(self, line_left):
        line_left = self.key_word_filter(line_left)
        ret = {}
        if line_left[0] != ')' and line_left.find('void') < 0:
            line_left = line_left[:line_left.rfind(')')]
            if line_left.strip() != 'VO':
                args_tmp = line_left.split(',')
                for arg_tmp in args_tmp:
                    name_to_filt , dont_care_result , type_name = self.find_type(arg_tmp.strip())
                    arg_name = '_arg_' + name_filter(name_to_filt)
                    ret.update({arg_name.strip():type_name})
        return ret

    def function_analyze(self, line_left):
        self.current_handler = None
        self.unit_handled = True
        if self.is_in_function is True:
            if self.func_line_cnt == 0:
                self.function_tmp.defined_path = self.current_path
                if line_left[-1] == '}':
                    '''
                    function with only one line, will not beening analyzed.
                    '''
                    self.module_info.function_list.append(self.function_tmp)
                    self.function_tmp = None
                    self.is_in_function = False
                    return ""
                else:
                    '''
                    read arguments
                    '''
                    self.function_tmp.func_content.append(self.function_line)
                    self.function_tmp.arg_infos = self.function_read_args(
                        line_left.strip())
            else:
                ret = self.get_code_sentence(line_left)
                if len(ret) > 0:
                    for line in ret:
                        self.line_anlyze(line)
            self.func_line_cnt += 1
            line_left = ""
        return line_left
